package _effx.greensock
{
   import com.greensock.TweenLite;
   import com.greensock.plugins.TweenPlugin;
   
   import flash.display.DisplayObject;
   import flash.display.DisplayObjectContainer;
   import flash.display.Sprite;
   import flash.geom.PerspectiveProjection;
   import flash.geom.Point;
   import flash.utils.Dictionary;
   
   /**
    * Objects will get blurry when we put them from 2d-space to 3d-space by changing their rotationX, rotationY, rotationZ and z properties.
    * Actually you can solve this issue by reseting the matrix3D to null and the object will be put from 3d-space back to 2d-space. But its
    * position will be reset to original point as well. So you need to put the object to the place where it should be.</br>
    * With this plugin you won't worry about those complicate issues. And it provides independent vanishing point for each objects.</br></br>
    * <b>Example:</b><br/><br/>
    * <code>
    * import com.greensock.plugins.TweenPlugin;</br>
    * import com.greensock.plugins.Adobe3DPlugin;</br>
    * TweenPlugin.activate([Adobe3DPlugin]);</br>
    * </br>
    * TweenMax.to(mc1, 0.5, {adobe3d:{rx:100, ry:5, rz:60}});</br>
    * TweenMax.to(mc1, 0.5, {adobe3d:{rx:100, ry:5, rz:60, fov:60}}); // With given "fov" property, plugin will create a proxy container between the object and its original parent and it has independt vanishing point as well.</br>
    * </code>
    * </br>
    * @author boy, cjboy1984@gmail.com 
    */
   public class Adobe3DPlugin extends TweenPlugin
   {
      public static const VERSION:Number = 1.0;
      public static const API:Number = 1.0;
      
      private var ta:DisplayObject;
      private var startRX:Number;
      private var startRY:Number;
      private var startRZ:Number;
      private var endRX:Number;
      private var endRY:Number;
      private var endRZ:Number;
      private var diffRX:Number;
      private var diffRY:Number;
      private var diffRZ:Number;
      
      // target's parent
      private var parent:DisplayObjectContainer;
      private var layerNo:int;
      // A proxy container, only exist when "fov" was set.
      private var proxy:Sprite;
      private static var proxyPool:Dictionary = new Dictionary();
      
      public function Adobe3DPlugin()
      {
         super();
         propName = "adobe3d";
         overwriteProps = ['adobe3d'];
         onComplete = destroyAll;
         onDisable = destroyAll;
      }
      
      // --------------------- LINE ---------------------
      
      override public function onInitTween(target:Object, value:*, tween:TweenLite):Boolean
      {
         if (!(target is DisplayObject)) return false; // DisplayObject limit
         if (typeof(value) != "object") return false; // Object limit
         
         ta = DisplayObject(target);
         
         startRX = ta.rotationX;
         startRY = ta.rotationY;
         startRZ = ta.rotationZ;
         
         endRX = Object(value).hasOwnProperty('rx') ? value.rx : startRX;
         endRY = Object(value).hasOwnProperty('ry') ? value.ry : startRY;
         endRZ = Object(value).hasOwnProperty('rz') ? value.rz : startRZ;
         
         diffRX = endRX - startRX;
         diffRY = endRY - startRY;
         diffRZ = endRZ - startRZ;
         
         if (Object(value).hasOwnProperty('fov') && ta.parent)
         {
            if (proxyPool[ta])
            {
               proxy = Sprite(proxyPool[ta]);
               
               parent = proxy.parent;
               layerNo = parent.getChildIndex(proxy);
            }
            else
            {
               parent = ta.parent;
               layerNo = parent.getChildIndex(ta);
               
               proxy = new Sprite();
               proxy.transform.perspectiveProjection = new PerspectiveProjection();
               proxy.transform.perspectiveProjection.projectionCenter = new Point(ta.x, ta.y);
               proxy.transform.perspectiveProjection.fieldOfView = value.fov;
               
               proxy.addChild(ta);
               parent.addChildAt(proxy, layerNo);
               
               proxyPool[ta] = proxy;
            }
         }
         
         return true;
      }
      
      // --------------------- LINE ---------------------
      
      override public function get changeFactor():Number { return super.changeFactor; }
      override public function set changeFactor(value:Number):void
      {
         super.changeFactor = value;
         
         if (proxy)
         {
            proxy.transform.perspectiveProjection.projectionCenter = new Point(ta.x, ta.y);
         }
         
         ta.rotationX = value * diffRX + startRX;
         ta.rotationY = value * diffRY + startRY;
         ta.rotationZ = value * diffRZ + startRZ;
         
      }
      
      // ################### protected ##################
      
      // #################### private ###################
      
      private function destroyAll():void
      {
         if (endRX%360 == 0 && endRY%360 == 0 && endRZ%360 == 0)
         {
            var tmpX:Number = ta.x;
            var tmpY:Number = ta.y;
            ta.transform.matrix3D = null;
            ta.x = tmpX;
            ta.y = tmpY;
            
            if (proxy && proxy.parent) removeProxy();
         }
      }
      
      private function removeProxy():void
      {
         parent.removeChild(proxy);
         parent.addChildAt(ta, layerNo);
         
         delete proxyPool[ta];
         proxy = null;
         
         for (var key:Object in proxyPool)
         {
            if (key.parent == null)
            {
               delete proxyPool[key];
            }
         }
      }
      
      // --------------------- LINE ---------------------
      
   }
   
}